<?php
/**
 * @author all@cosmos-framework.org
 * @copyright (c) 2013 cosmos-framework.org
 * @license http://www.opensource.org/licenses/mit-license.php
 * @link http://cosmos-framework.org/
 * @package cosmos.framework
 * @version 2.0
 */
namespace Cosmos;

class Template {

    private $_readdir;

    /**
     * @param string $methodName
     * @param mixed[] $methodArgs
     * @return mixed
     */
    public static function __callStatic($methodName, $methodArgs) {

        return call_user_func_array($methodName, $methodArgs);
    }

    /**
     * @param string $statement
     * @return string
     */
    public static function compile($statement) {

        // remove comments.
        $_ = preg_replace('/\/\*.*?\*\//s', '', $statement);

        // {{ statement|() }} > {{ (statement) }}
        $_ = preg_replace_callback('/{{\s*([^}]+?)\|([^{]+?)\s*}}/', function($p) {

            $response = $p[1];
            foreach (explode('|', $p[2]) as $f) {
                $response = 'self::' . $f . '(' . $response . ')';
            }

            // response statement.
            return '{{' . $response . '}}';

        }, $_);

        // {{ statement }} > {% echo statement %}
        $_ = preg_replace('/{{\s*(.+?)\s*}}/', '{% echo $1 %}', $_);

        // {% statement %} > <?php statement;? >
        $_ = preg_replace_callback('/{%\s*(.*?)\s*%}/s', function($p) {

            // mapped list.
            return sprintf('<?php %s;?>', preg_replace_callback('/(\$[\-\w\[\]\.]+)/', function($p) {

                // mapped list.
                return preg_replace('/\.(\w+)/', '[\'$1\']', $p[1]);

            }, $p[1]));

        }, $_);

        return $_;
    }

    /**
     * @param string $v
     * @return string
     */
    public static function escape($v) {

        return preg_replace('/&amp;(#[0-9]+|[a-z]+);/i', '&$1;', htmlspecialchars($v, ENT_QUOTES));
    }

    /**
     * @uses $_SERVER.DOCUMENT_ROOT
     */
    public static function handle() {

        global $_INDEX;

        // initialize readable.
        $l = preg_replace('/\/$/', '/' . $_INDEX, URL::path());

        // sanitize readable.
        $l = str_replace('/../', '/', $l);

        $c = new self($_SERVER['DOCUMENT_ROOT']);

        try {
            echo $c->fetch($l);
        } catch(Exception $e) {
            $c->fail($e);
        } catch(\Exception $e) {
            $c->fail(new Exception($e->getMessage(), 500, $e));
        }
    }

    /**
     * @param string $readdir
     */
    public function __construct($readdir = null) {

        $this->_readdir = $readdir;
    }

    /**
     * fail response.
     */
    public function fail(Exception $e) {

        global $_CAUSE, $_ERRDOCS;

        // fail message.
        $_CAUSE = $e;
        if (!empty($_ERRDOCS[$e->code()])) {
            $e->message($this->fetch($_ERRDOCS[$e->code()]));
        }

        // fail response.
        throw $e;
    }

    /**
     * @param string $readable
     * @return string
     */
    public function fetch($readable) {

        return $this->_eval(self::compile(file_get_contents(self::_negotiate(Assert::is($this->_readdir . $readable, 'is_file($v)', 'Not found.', 404)))));
    }

    /**
     * @param string $readable
     * @return string
     */
    private static function _negotiate($readable) {

        $a = &$_SERVER['HTTP_ACCEPT_LANGUAGE'];

        $l = substr($a, 0, 2);
        if (is_readable($readable . '.' . $l)) {
            return $readable . '.' . $l;
        }

        return $readable;
    }

    /**
     * @param string $statement
     * @return string
     */
    private function _eval($statement) {

        // eval statement.
        $response = eval('ob_start();?>' . $statement . '<?php return ob_get_contents();');
        ob_end_clean();

        // eval response.
        return $response;
    }

}
